class StaticStruct  
  def initialize(hash)
    if !hash.is_a?(Hash)
      raise "StaticStruct must be initialized with a hash."
    end

    @table = build(hash)
  end
  
  def method_missing(mid, *args) 
    if args.size > 0
       raise "Can't assign or create attribute #{mid} because StaticStruct is immutable."
    end
    
    value_of(mid)
  end

  def [](key)
    value_of(key)
  end

 def value_of(key)
    if !@table.has_key?(key.to_sym)
      raise "Property #{key} was not found for StaticStruct##{object_id}."
    end

    @table[key.to_sym]
  end

  def ==(other)
    return false unless(other.is_a?(StaticStruct))
    return self.to_h == other.to_h
  end

  def to_h
    Hash[@table.map {|k, v| [k.to_sym, build_hash(v)]}]
  end

  def to_json(options = nil)
    to_h.to_json
  end
  
  def as_json(options = nil)
    to_h.as_json
  end

  private

  def build(node) 
    case node when Hash 
      Hash[node.map {|k,v| [k.to_sym, build_object(v)]}]
    when Array 
      node.map { |item| build_object(item) } 
    else 
      node
    end
  end

  def build_object(node)
    case node when Hash 
      StaticStruct.new(node)
    else 
      build(node)
    end
  end

  def build_hash(node)
    case node when Array 
      node.map { |item| build_hash(item) }
    else 
      node.methods.include?(:to_h) ? node.to_h : node
    end
  end
end